﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Arbore;
using Satates;
using Operators;
using Operators.CMOperators;
using Common;

namespace Algorithms.Bidirectional
{
    public class BidirectionalAlgo:Algorithm
    {
        private bool switcher1;
        private bool switcher2;
        private bool result = false;
        private Stack<State> rStack;
        Stack<State> results = new Stack<State>();
        private Arbore.Arbore ab;

        public BidirectionalAlgo(State iState, List<State> fStates, List<iOperator> opr, List<string> log,
            ref Arbore.Arbore Ab) : base(iState, fStates, opr, log)
        {
            ab = Ab;
        }
        // TODO Auto-generated constructor stub

        private bool expand(State thisState)
        {
            int counter = 0;
            if (switcher1)
            {
                counter = getIndex(thisState.RedoOperator)+1 ; switcher1 = false; 
            }
            if (getIndex(thisState.CurrentOperator) == operatorNumber) { counter = 0; }
            Nod a = ab.CurrentNod; 
            for (int i = counter; i <= operatorNumber; i++)
            {
                if (operators[i].available(thisState.CurrentState))
                {
                    var m = (Template)thisState.CurrentState.Clone();
                    State auxiliary = new State(operators.ElementAt(i).execute(m), operators.ElementAt(i));
                    ab.CurrentNod = a;
                    ab.AddS(new Nod(operators.ElementAt(i).ToString() + Environment.NewLine + auxiliary.ToString()));
                    if (!found(auxiliary)) {
                        btStack.Push(auxiliary);
                        
                        return true; }
                }
            }
            return false;
        }

        private bool expandR(State thisState)
        {
            int counter = 0;
            if (switcher2)
            {
                counter = getIndex(thisState.RedoOperator) ; switcher2=false;
            }
            if (getIndex(thisState.CurrentOperator) == operatorNumber) { counter = 0; }
            Nod a = ab.CurrentNod; 
            for (int i = counter; i <= operatorNumber; i++)
            {
                if (operators[i].available(thisState.CurrentState))
                {
                    var m = (Template)thisState.CurrentState.Clone();
                    State auxiliary = new State(operators.ElementAt(i).execute(m), operators.ElementAt(i));
                  
                    if (!foundR(auxiliary)) { 
                        rStack.Push(auxiliary);
                       
                        return true; }
                }
            }
            return false;
        }
        private bool foundR(State auxiliary)
        {
            foreach (var it in rStack)
            {
                 if (auxiliary.ToString().Equals(it.ToString())) return true;
            }
            /*for (StackIterator<State<Template>> it = new StackIterator<State<Template>>(rStack); it.hasNext(); )
            {
                if (auxiliary.equals(it.next())) return true;
            }*/
            return false;
        }

        private bool solve(State thisState1, State thisState2)
        {

            if (!switcher1) switcher1=false;
            if (!switcher2) switcher2=false;
            if (thisState1 != null && thisState2 != null)
            {
                if (expand(thisState1))
                {
                    string s = solutionFound();
                    if (s.Length > 1) { printresult(s,ref ab ); result = true; return true; };
                }
                else
                {
                    iOperator auxiliaryOperator = btStack.Peek().CurrentOperator;
                    if (btStack.Count() > 1)
                    {
                        btStack.Pop();
                    }
                    State auxiliaryState = btStack.Peek();
                    btStack.Pop();
                    auxiliaryState.RedoOperator=auxiliaryOperator;
                    btStack.Push(auxiliaryState);
                    switcher1=true;

                }
                if (expandR(thisState2))
                {
                    String s = solutionFound();
                    if (s.Length > 1) { printresult(s,ref ab); result = true; return true; };
                }
                else
                {
                     iOperator auxiliaryOperator = rStack.Peek().CurrentOperator;
                    if (rStack.Count() > 1)
                    {
                        rStack.Pop();
                    }
                    State auxiliaryState = rStack.Peek();
                    rStack.Pop();
                    auxiliaryState.RedoOperator=auxiliaryOperator;
                    rStack.Push(auxiliaryState);
                    switcher2=true;

                }
                solve(btStack.Peek(), rStack.Peek());
            } return false;
        }
        private string solutionFound()
        {
            for (int i = 0; i < btStack.Count(); i++)
                for (int j = 0; j < rStack.Count(); j++)
                    if (btStack.ElementAt(i).CurrentState.ToString().Equals(rStack.ElementAt(j).CurrentState.ToString()))
                        return i + "@" + j;
            return "";
        }

        private void printresult(string s,ref Arbore.Arbore ab)
        {
            int a, b;
            a = int.Parse(s.Substring(0, s.IndexOf('@')));
            b = int.Parse(s.Substring(s.IndexOf('@') + 1));
            Stack<State> results = new Stack<State>();
            for (int j = 0; j < rStack.Count ; j++)
            {
                rStack.ElementAt(j).CurrentOperator=rStack.ElementAt(j ).CurrentOperator;
            }
            for (int i = 0; i < a; i++)
            {
                results.Push(btStack.ElementAt(i));
            }
            for (int j = b; j >= 0; j--)
            {
                results.Push(rStack.ElementAt(j));
            }

            iOperator op = findOperator(btStack.ElementAt(a), rStack.ElementAt(b));
            btStack = new Stack<State>();

            foreach (var it in results)
            {
              
                ab.AddS(new Nod(it.CurrentOperator.ToString()+ Environment.NewLine + it.CurrentState.ToString()));
                btStack.Push(it);
            }
            btStack.ElementAt(a).CurrentOperator=op;

        }

        private iOperator findOperator(State thisState1, State thisState2)
        {
            for (int i = 0; i <= operatorNumber; i++)
            {
                if (operators.ElementAt(i).available(thisState1.CurrentState))
                {
                    State auxiliary = new State(operators.ElementAt(i).execute(thisState1.CurrentState), operators.ElementAt(i));

                    if (auxiliary.equals(thisState2)) return operators.ElementAt(i);
                }
            }
            return null;
        }
        public override bool solveProblem()
        {
            ab.AddS(new Nod(initialState.ToString()));
            solve(initialState, finalStates[0]);
            if (result == true) return true;
            return false;
        }
        public override void init()
        {
            results = new Stack<State>();
            rStack = new Stack<State>();
            State aux = finalStates[0];
            aux.CurrentOperator=((iOperator)(new InitialState()));
            rStack.Push(finalStates[0]);
            switcher1=false;
            switcher2=false;
        }


    }
}
